#include "gstreamer_4.h"

typedef struct _CustomData
{
    GstElement *playbin;
    gboolean playing;
    gboolean terminate;
    gboolean seek_enabled;
    gboolean seek_done;
    gint64 duration;

} CustomData;

static void handle_message(GstMessage *message, CustomData *data);

int play_03(int argc, char *argv[])
{
    CustomData data;
    GstBus *bus;
    GstMessage *message;
    GstStateChangeReturn ret;

    data.playing = false;
    data.terminate = false;
    data.seek_enabled = false;
    data.seek_done = false;
    data.duration = GST_CLOCK_TIME_NONE;

    gst_init(&argc, &argv);

    data.playbin = gst_element_factory_make("playbin", "playbin");

    if (!data.playbin)
    {
        g_printerr("Not all elements could be created!\n");
        return -1;
    }

    g_object_set(data.playbin, "uri", "https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.webm", nullptr);

    ret = gst_element_set_state(data.playbin, GST_STATE_PLAYING);
    if (ret == GST_STATE_CHANGE_FAILURE)
    {
        g_printerr("Unable to set the pipeline to playing state.\n");
        g_object_unref(data.playbin);
        return -1;
    }

    bus = gst_element_get_bus(data.playbin);
    do
    {
        message = gst_bus_timed_pop_filtered(bus, 100 * GST_MSECOND, (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS | GST_MESSAGE_STATE_CHANGED | GST_MESSAGE_DURATION));

        if (message)
        {
            handle_message(message, &data);
            gst_message_unref(message);
        }
        else
        {
            if(data.playing) {
                gint64 current = -1;

                if(!gst_element_query_position(data.playbin, GST_FORMAT_TIME, &current)) {
                    g_printerr("Could not query current position.\n");
                }

                if(!GST_CLOCK_TIME_IS_VALID(data.duration)) {
                    if(!gst_element_query_duration(data.playbin, GST_FORMAT_TIME, &data.duration)) {
                        g_printerr("Could not query current duration.\n");
                    }
                }

                g_print("Position %" GST_TIME_FORMAT "/ %" GST_TIME_FORMAT "\r", GST_TIME_ARGS(current), GST_TIME_ARGS(data.duration));

                if(data.seek_enabled && !data.seek_done && current > 10 * GST_SECOND) {
                    g_print("\n Reached 10s, performing seek...\n");
                    gst_element_seek_simple(data.playbin, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), 30 * GST_SECOND);
                    data.seek_done = true;
                }
            }
        }
    } while (!data.terminate);

    gst_object_unref(bus);
    gst_element_set_state(data.playbin, GST_STATE_NULL);
    gst_object_unref(data.playbin);

    return 0;
}

static void handle_message(GstMessage *message, CustomData *data)
{
    GError *err;
    char *debug_info;

    switch (GST_MESSAGE_TYPE(message))
    {
    case GST_MESSAGE_ERROR:
        gst_message_parse_error(message, &err, &debug_info);
        g_printerr("Error received from element %s:%s\n", GST_OBJECT_NAME(message->src), err->message);
        g_printerr("Debugging information: %s\n", debug_info ? debug_info : "none");
        g_clear_error(&err);
        g_free(debug_info);
        data->terminate = true;
        break;
    case GST_MESSAGE_EOS:
        g_print("End of stream has been reached!\n");
        data->terminate = true;
        break;
    case GST_MESSAGE_STATE_CHANGED:
        if (GST_MESSAGE_SRC(message) == GST_OBJECT(data->playbin))
        {
            GstState old_state, new_state, pending_state;
            gst_message_parse_state_changed(message, &old_state, &new_state, &pending_state);
            g_print("Pipeline state change from %s to %s.\n", gst_element_state_get_name(old_state), gst_element_state_get_name(new_state));

            data->playing = (new_state == GST_STATE_PLAYING);

            if (data->playing)
            {
                GstQuery *query;
                gint64 start, end;

                query = gst_query_new_seeking(GST_FORMAT_TIME);
                if (gst_element_query(data->playbin, query))
                {
                    gst_query_parse_seeking(query, nullptr, &data->seek_enabled, &start, &end);

                    if (data->seek_enabled)
                    {
                        g_print("Seeking is ENABLED from %" GST_TIME_FORMAT "to %" GST_TIME_FORMAT "\n", GST_TIME_ARGS(start), GST_TIME_ARGS(end));
                    }
                    else
                    {
                        g_print("Seeking is DISABLED for this stream.\n");
                    }
                }
                else
                {
                    g_printerr("Seeking is query failed");
                }
                gst_query_unref(query);
            }
        }
        break;
    case GST_MESSAGE_DURATION:
        data->duration = GST_CLOCK_TIME_NONE;
        break;
    default:
        g_printerr("Unexcepted message received!\n");
        break;
    }
}